/*******************************************************************************
 * Copyright (c) 2014 Joel Thurlby.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Joel Thurlby - initial API and implementation
 ******************************************************************************/
package jat.xtext.gantt.ui.diagram;

import jat.xtext.gantt.ganttText.Model;
import jat.xtext.gantt.ganttText.Task;
import jat.xtext.gantt.ganttText.WorkPackage;

import java.util.Iterator;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.nebula.widgets.ganttchart.DefaultSettings;
import org.eclipse.nebula.widgets.ganttchart.GanttChart;
import org.eclipse.nebula.widgets.ganttchart.GanttEvent;
import org.eclipse.nebula.widgets.ganttchart.themes.ColorThemeWindowsBlue;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ViewForm;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.ui.editor.XtextEditor;
import org.eclipse.xtext.ui.editor.model.IXtextDocument;
import org.eclipse.xtext.ui.editor.model.IXtextModelListener;
import org.eclipse.xtext.ui.editor.utils.EditorUtils;
import org.eclipse.xtext.util.concurrent.IUnitOfWork;
import org.eclipse.wb.swt.SWTResourceManager;

public class GanttChartViewer extends ViewPart implements IXtextModelListener {
	public static final String ID = "jat.xtext.gantt.views.GanttView";

	/**
	 * The GanttText language identifier.
	 */
	private final String JAT_XTEXT_GANTT_GANTTTEXT = "jat.xtext.gantt.GanttText";

	/**
	 * The current GanttText document.
	 */
	private IXtextDocument m_document;

	/**
	 * The GanttChart used by this ViewPart.
	 */
	private GanttChart m_ganttChart;

	/**
	 * Handles GanttText model change notifications.
	 */
	private GanttEditorListener m_editorListener;

	/**
	 * Handles GanttChart GUI events.
	 */
	private GanttChartListener m_chartListener;

	/**
	 * Allocates GanttChart artifacts into the view's GanttChart based on the
	 * current GanttText document.
	 */
	private GanttChartPreprocessor m_preprocessor;

	public GanttChartViewer() {
		m_editorListener = new GanttEditorListener(this);
		m_chartListener = new GanttChartListener(this);
		m_preprocessor = new GanttChartPreprocessor();
	}

	@Override
	public void createPartControl(Composite parent) {
		// The editor listener will update the viewer when the current gantt
		// editor changes.
		final IWorkbenchWindow workbenchWindow = getSite().getWorkbenchWindow();
		workbenchWindow.getPartService().addPartListener(m_editorListener);

		ViewForm viewForm = new ViewForm(parent, SWT.NONE);

		ToolBar toolBar = new ToolBar(viewForm, SWT.FLAT | SWT.RIGHT);
		viewForm.setTopRight(toolBar);

		ToolItem tltmNewItem = new ToolItem(toolBar, SWT.NONE);
		tltmNewItem.setText("Export Diagram");
		tltmNewItem.addSelectionListener(new SelectionListener() {

			@Override
			public void widgetSelected(SelectionEvent e) {
				FileDialog dialog = new FileDialog(workbenchWindow.getShell(), SWT.OPEN);
				dialog.setFilterExtensions(new String[] { "*.png" });
				final String result = dialog.open();

				if (result == null) {
					return;
				}

				Display.getDefault().asyncExec(new Runnable() {
					@Override
					public void run() {
						Image img = m_ganttChart.getGanttComposite().getImage();
						if (img == null) {
							return;
						}
						ImageLoader loader = new ImageLoader();
						loader.data = new ImageData[] { img.getImageData() };
						loader.save(result, SWT.IMAGE_PNG);
						img.dispose();
					}
				});
			}

			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
			}
		});
		
		m_ganttChart = new GanttChart(viewForm, SWT.NONE, new DefaultSettings(), new ColorThemeWindowsBlue());
		m_ganttChart.getGanttComposite().setForeground(SWTResourceManager.getColor(SWT.COLOR_BLACK));
		m_ganttChart.getGanttComposite().setBackground(SWTResourceManager.getColor(SWT.COLOR_WHITE));
		m_ganttChart.setTouchEnabled(true);
		m_ganttChart.setBackground(SWTResourceManager.getColor(SWT.COLOR_WIDGET_LIGHT_SHADOW));

		viewForm.setContent(m_ganttChart);
		// The chart listener will update the editor when an artifact is
		// selected.
		m_ganttChart.addGanttEventListener(m_chartListener);
		m_ganttChart.getGanttComposite().setView(2);
		// If a GanttText editor is opened, then push it into the chart
		// viewer. Otherwise, leave the GanttChart empty.
		XtextEditor editor = EditorUtils.getActiveXtextEditor();
		if ((editor != null) && JAT_XTEXT_GANTT_GANTTTEXT.equals(editor.getLanguageName())) {
			loadModel(editor.getDocument());
		}
	}

	@Override
	public void dispose() {
		m_document.removeModelListener(this);
	}

	/**
	 * Loads a new GanttText document into the view's GanttChart.
	 * 
	 * @param document
	 *            The GanttText document to load into the GanttChart.
	 */
	public void loadModel(IXtextDocument document) {
		if (m_document != null) {
			m_document.removeModelListener(this);
		}
		m_document = document;
		m_document.addModelListener(this);

		document.readOnly(new IUnitOfWork.Void<XtextResource>() {
			@Override
			public void process(XtextResource state) throws Exception {
				reloadModel(state);
			}
		});
	}

	/**
	 * Push the new changes of the current document into the GanttChart.
	 * 
	 * TODO: This implementation re-processes the *entire* document!
	 */
	@Override
	public void modelChanged(final XtextResource resource) {
		Display.getDefault().asyncExec(new Runnable() {
			@Override
			public void run() {
				reloadModel(resource);
			}
		});
	}

	/**
	 * Clears the Gantt chart artifacts and loads the XtextResource into the
	 * chart.
	 * 
	 * Note: This method shall always be executed asynchronously.
	 * 
	 * @param state
	 */
	private void reloadModel(XtextResource state) {
		if (!state.getErrors().isEmpty() || (m_ganttChart == null) || m_ganttChart.isDisposed()
				|| m_ganttChart.getGanttComposite().isDisposed()) {
			// Only process the document when there are no errors.
			return;
		}

		// EMPTY CHART
		m_ganttChart.getGanttComposite().getGanttConnections().clear();
		m_ganttChart.getGanttComposite().getEvents().clear();
		m_ganttChart.getGanttComposite().getGanttSections().clear();

		// POPULATE CHART
		for (Iterator<EObject> iter = state.getAllContents(); iter.hasNext();) {
			EObject obj = iter.next();
			if (obj instanceof Model) {
				Model model = (Model) obj;
				m_preprocessor.populateChart(model.getProjects().get(0), m_ganttChart);
			}
		}

		// REDRAW CHART
		m_ganttChart.redrawGanttChart();
		m_ganttChart.redraw();
	}

	@Override
	public void setFocus() {
		// Nothing to do here. Move along.
	}

	/**
	 * Clears all artifacts within the GanttChart. Also redraws the empty
	 * GanttChart.
	 */
	public void clearChart() {
		Display.getDefault().asyncExec(new Runnable() {
			@Override
			public void run() {
				if ((m_ganttChart == null) || m_ganttChart.isDisposed() || m_ganttChart.getGanttComposite().isDisposed()) {
					return;
				}

				m_ganttChart.getGanttComposite().getGanttConnections().clear();
				m_ganttChart.getGanttComposite().getEvents().clear();
				m_ganttChart.getGanttComposite().getGanttSections().clear();
				m_ganttChart.redrawGanttChart();
				m_ganttChart.redraw();
			}
		});
	}

	/**
	 * Highlights the selected task within current GanttChart.
	 * 
	 * @param task
	 *            The selected task.
	 */
	public void taskSelected(Task task) {
		GanttEvent event = m_preprocessor.getEvent(task);
		if ((event == null) || m_ganttChart.isDisposed() || m_ganttChart.getGanttComposite().isDisposed()) {
			return;
		}

		m_ganttChart.getGanttComposite().setSelection(event);
		m_ganttChart.getGanttComposite().setTopItem(event, -100, -150);
		m_ganttChart.getGanttComposite().setDate(event.getStartDate(), true);
	}

	public void workPackageSelected(WorkPackage workPackage) {
		GanttEvent event = m_preprocessor.getEvent(workPackage);
		if ((event == null) || m_ganttChart.isDisposed() || m_ganttChart.getGanttComposite().isDisposed()) {
			return;
		}

		m_ganttChart.getGanttComposite().setSelection(event);
		m_ganttChart.getGanttComposite().setTopItem(event, -100, -150);
		m_ganttChart.getGanttComposite().setDate(event.getStartDate(), true);
	}
}
